; (function ($) {
    $.tankBattles = $.tankBattles || {};

    // Enums and constants
    var direction = $.tankBattles.enum.direction,

    // Objects
    bg,
	player,
	tanks = [],
	rockets = [],
	staticObjs = [],
	// All objects, that should be rendered
	gameObjs = [],
    gameFactory,

	// ------------------------------------------ //
	beforeRenderHandler = function (dt, keysDown) {
	    for (var i = 0, len = tanks.length; i < len; i++) {
	        var t = tanks[i],
                x = t.x,
				y = t.y;

	        t.weaponReload(dt);
	        t.move(dt, keysDown);

	        // interactions with static objects and other tanks
	        if (t.x < 0
				|| t.x + t.width > bg.width
				|| t.y < 0
				|| t.y + t.height > bg.height
				|| canMove(t, tanks)
				|| canMove(t, staticObjs)) {
	            // revert x and y
	            t.x = x;
	            t.y = y;
	            if (t != player) {
	                t.cannotMove(dt);
	            }
	        }
	    }

	    for (var i = 0, len = rockets.length; i < len; i++) {
	        rockets[i].move(dt);
	    }
	},
    rocketsDestroyed = [],
	tanksDestroyed = [],
	staticObjsDestroyed = [],
    afterRenderHandler = function (dt, keysDown) {
        var destroyRocket = function (rocket) {
            gameFactory.destroyRocket(rocket, function (animation) {
                gameObjs.push(animation);
            },
            function (animation) {
                var index = gameObjs.indexOf(animation);
                gameObjs.slice(index, 1);
            });
            rocketsDestroyed.push(rocket);
	    },
	    tankFacedWithARocket = function (rocket, tank) {
	        tank.health -= (rocket.damage - tank.armor);
	        if (tank.health <= 0) {
	            tanksDestroyed.push(tank);
	        }
	    },
        staticObjectFacedWithARocket = function (rocket, staticObject) {
            staticObject.health -= rocket.damage;
            if (staticObject.health <= 0) {
                staticObjsDestroyed.push(staticObject);
            }
        };

	    for (var i = 0, len = rockets.length; i < len; i++) {
	        var rocket = rockets[i];
	        if (rocket.x < 0
                || rocket.x + rocket.width > bg.width
                || rocket.y < 0
                || rocket.y + rocket.height > bg.height
                || hasIntersections(rocket, rockets)
                || hasIntersections(rocket, tanks, tankFacedWithARocket)
                || hasIntersections(rocket, staticObjs, staticObjectFacedWithARocket)) {
	            destroyRocket(rocket);
	        }
	    }

	    var i = 0, len = 0;
	    for (i = 0, len = rocketsDestroyed.length; i < len; i++) {
	        removeGameObject(rocketsDestroyed[i], rockets);
	    }
	    rocketsDestroyed.splice(0, len);

	    for (i = 0, len = tanksDestroyed.length; i < len; i++) {
	        removeGameObject(tanksDestroyed[i], tanks);
	    }
	    tanksDestroyed.splice(0, len);

	    for (i = 0, len = staticObjsDestroyed.length; i < len; i++) {
	        removeGameObject(staticObjsDestroyed[i], staticObjs);
	    }
	    staticObjsDestroyed.splice(0, len);
	},
    removeGameObject = function (item, items) {
        var index = items.indexOf(item);
        if (index >= 0) {
            items.splice(index, 1);
        }
        index = gameObjs.indexOf(item);
        if (index >= 0) {
            gameObjs.splice(index, 1);
        }
    },
	canMove = function (source, targets) {
	    for (var i = 0, len = targets.length; i < len; i++) {
	        var t = targets[i];
	        if (source === t || t instanceof Forest) continue;
	        var hasXIntersections = ((source.x < t.x && source.x + source.width > t.x) || (t.x < source.x && t.x + t.width > source.x));
	        var hasYIntersections = ((source.y < t.y && source.y + source.height > t.y) || (t.y < source.y && t.y + t.height > source.y));
	        if (hasXIntersections && hasYIntersections) return true;
	    }
	    return false;
	},
	hasIntersections = function (source, targets, callback) {
	    for (var i = 0, len = targets.length; i < len; i++) {
	        var t = targets[i];
	        if (t instanceof Water || t instanceof Forest) continue;
	        var hasXIntersections = ((source.x < t.x && source.x + source.width > t.x) || (t.x < source.x && t.x + t.width > source.x));
	        var hasYIntersections = ((source.y < t.y && source.y + source.height > t.y) || (t.y < source.y && t.y + t.height > source.y));
	        if (hasXIntersections && hasYIntersections) {
	            if (typeof callback === 'function') {
	                callback(source, t);
	            }
	            return true;
	        }
	    }
	    return false;
	};

    $.tankBattles.game = {
        start: function (wrapperId, factory) {
            gameFactory = factory;

            var beforeFireEvent = function () {
            },
			afterFireEvent = function (rocket) {
			    if (rocket != null) {
			        rockets.push(rocket);
			        gameObjs.push(rocket);
			    }
			};
            // INIT OBJECTS
            bg = factory.createBackground();
            gameObjs.push(bg);

            player = factory.createPlayer();
            player.beforeFireEvent = beforeFireEvent;
            player.afterFireEvent = afterFireEvent;
            gameObjs.push(player);

            tanks = factory.createEnemies();
            tanks.push(player);
            for (var i = 0, len = tanks.length; i < len; i++) {
                tanks[i].beforeFireEvent = beforeFireEvent;
                tanks[i].afterFireEvent = afterFireEvent;
                gameObjs.push(tanks[i]);
            }

            staticObjs = factory.createMapObjects();
            for (var i = 0, len = staticObjs.length; i < len; i++) {
                gameObjs.push(staticObjs[i]);
            }

            var options = {
                gameObjs: gameObjs,
                beforeRenderEvent: beforeRenderHandler,
                afterRenderEvent: afterRenderHandler
            };
            $.tankBattles.core.register(wrapperId, bg.width, bg.height, options);
            options.startGame();
        }
    };
})(epam = typeof epam !== "undefined" ? epam : {});
